package com.wyr.leetcode.step2.multiThread;

import java.util.concurrent.Semaphore;


/**
 * 现有函数 printNumber 可以用一个整数参数调用，并输出该整数到控制台。
 * <p>
 * 例如，调用 printNumber(7) 将会输出 7 到控制台。
 * 给你类 ZeroEvenOdd 的一个实例，该类中有三个函数：zero、even 和 odd 。ZeroEvenOdd 的相同实例将会传递给三个不同线程：
 * <p>
 * 线程 A：调用 zero() ，只输出 0
 * 线程 B：调用 even() ，只输出偶数
 * 线程 C：调用 odd() ，只输出奇数
 * 修改给出的类，以输出序列 "010203040506..." ，其中序列的长度必须为 2n 。
 * <p>
 * 实现 ZeroEvenOdd 类：
 * <p>
 * ZeroEvenOdd(int n) 用数字 n 初始化对象，表示需要输出的数。
 * void zero(printNumber) 调用 printNumber 以输出一个 0 。
 * void even(printNumber) 调用printNumber 以输出偶数。
 * void odd(printNumber) 调用 printNumber 以输出奇数。
 * <p>
 * https://leetcode.cn/problems/print-zero-even-odd/description/
 */
public class ZeroEvenOdd2 {
    private volatile int count = 1;
    // 是否已经打印 0
    private volatile boolean flag = false;
    // zero令牌
    Semaphore zeroSemaphore = new Semaphore(1);
    // 偶数令牌
    Semaphore evenSemaphore = new Semaphore(0);
    // 奇数令牌
    Semaphore oddSemaphore = new Semaphore(0);


    private int n;

    public ZeroEvenOdd2(int n) {
        this.n = n;
    }

    // printNumber.accept(x) outputs "x", where x is an integer.
    public void zero(IntConsumer2 printNumber) throws InterruptedException {
        for (int i = 0; i < n; i++) {
            // 获取令牌,没有令牌使用会阻塞在此处
            zeroSemaphore.acquire(1);
            // 打印0
            printNumber.accept(0);
            if(count % 2 == 0){
                // 释放一个偶数令牌
                evenSemaphore.release(1);
            }else {
                // 释放一个奇数令牌
                oddSemaphore.release(1);
            }
        }
    }

    public void even(IntConsumer2 printNumber) throws InterruptedException {
        // 已经打印偶数的次数
        int printCount = 0;
        while (printCount < n / 2) {
            // 获取令牌,没有令牌使用会阻塞在此处
            evenSemaphore.acquire(1);
            if (count % 2 == 0) {
                printNumber.accept(count++);
                printCount++;
                zeroSemaphore.release(1);
            }
        }
    }


    public void odd(IntConsumer2 printNumber) throws InterruptedException {
        // 已经打印奇数的次数
        int printCount = 0;
        while (printCount < (n + 1) / 2) {
            // 获取令牌,没有令牌使用会阻塞在此处
            oddSemaphore.acquire(1);
            if (count % 2 != 0) {
                printNumber.accept(count++);
                printCount++;
                zeroSemaphore.release(1);
            }
        }
    }

    public static void main(String[] args) {
        ZeroEvenOdd2 zeroEvenOdd = new ZeroEvenOdd2(2);
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    zeroEvenOdd.zero(new IntConsumer2());
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "t1").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    zeroEvenOdd.even(new IntConsumer2());
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "t2").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    zeroEvenOdd.odd(new IntConsumer2());
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "t3").start();
    }
}

class IntConsumer2 {
    public void accept(int x) {
        System.out.println(x);
    }
}
